#if ! (UNITY_DASHBOARD_WIDGET || UNITY_WEBPLAYER || UNITY_WII || UNITY_WIIU || UNITY_NACL || UNITY_FLASH || UNITY_BLACKBERRY) // Disable under unsupported platforms.
#if UNITY_EDITOR
using System;
using System.IO;
using System.Xml;
using System.Xml.XPath;
using System.Xml.Serialization;
using UnityEditor;
using UnityEngine;
using System.Reflection;
using System.Collections.Generic;
using System.Runtime.InteropServices;

namespace AudioStudio
{
    
public class BankGenerator
{
	
    static Dictionary<string, string> s_ProjectBankPaths = new Dictionary<string, string>();
    static Dictionary<string, HashSet<string>> s_BaseToCustomPF = new Dictionary<string, HashSet<string>>();
    static public IDictionary<string, HashSet<string>> GetPlatformMapping()
    {
        return s_BaseToCustomPF;
    }
        
    public static bool IsSoundbankGenerationAvailable()
    {
        return (GetWwiseCLI() != null);
    }

    public static string ExecuteCommandLine(string command, string arguments)
    {
        string output = "";

        System.Diagnostics.Process process = new System.Diagnostics.Process();

        process.StartInfo.FileName = command;
        process.StartInfo.UseShellExecute = false;
        process.StartInfo.RedirectStandardOutput = true;
        process.StartInfo.CreateNoWindow = true;
        process.StartInfo.Arguments = arguments;
        process.Start();

        // Synchronously read the standard output of the spawned process. 
        StreamReader reader = process.StandardOutput;
        output = reader.ReadToEnd();

        // Waiting for the process to exit directly in the UI thread. Similar cases are working that way too.

        // TODO: Is it better to provide a timeout avoid any issues of forever blocking the UI thread? If so, what is
        // a relevant timeout value for soundbank generation?
        process.WaitForExit();
        process.Close();

        return output;
    }

    private static string GetWwiseCLI()
    {
        string result = null;

        AudioSetting settings = AudioSetting.LoadSettings();

#if UNITY_EDITOR_WIN

        if (!String.IsNullOrEmpty(settings.WwiseInstallationPathWindows))
        {
            result = Path.Combine(settings.WwiseInstallationPathWindows, @"Authoring\x64\Release\bin\WwiseCLI.exe");

            if (!File.Exists(result))
            {
                result = Path.Combine(settings.WwiseInstallationPathWindows, @"Authoring\Win32\Release\bin\WwiseCLI.exe");
            }
        }

#elif UNITY_EDITOR_OSX
        if (!String.IsNullOrEmpty(settings.WwiseInstallationPathMac))
        {
            result = Path.Combine(settings.WwiseInstallationPathMac, "Contents/Tools/WwiseCLI.sh");
        }
#endif

        if (result != null && File.Exists(result))
        {
            return result;
        }

        return null;
    }
    
    // Generate all the SoundBanks for all the supported platforms in the Wwise project. This effectively calls Wwise for the project
    // that is configured in the UnityWwise integration.
    public static void GenerateSoundbanks()
    {
        AudioSetting Settings = AudioSetting.LoadSettings();
        string wwiseProjectFullPath = AkUtilities.GetFullPath(Application.dataPath, Settings.WwiseProjectPath);
        
        if (IsSoundbankOverrideEnabled(wwiseProjectFullPath))
        {
            AkSoundEngine.LogWarning("The SoundBank generation process ignores the SoundBank Settings' Overrides currently enabled in the User settings. The project's SoundBank settings will be used.");
        }

        string wwiseCli = GetWwiseCLI();

        if (wwiseCli == null)
        {
            AkSoundEngine.LogError("Couldn't locate WwiseCLI, unable to generate SoundBanks.");
            return;
        }

        string command;
        string arguments;

#if UNITY_EDITOR_WIN
        command = wwiseCli;
        arguments = "";
#elif UNITY_EDITOR_OSX
        command = "/bin/sh";
        arguments = "\"" + wwiseCli + "\"";
#endif
        
        arguments += " \"" + wwiseProjectFullPath + "\" -GenerateSoundBanks";
        
        string output = ExecuteCommandLine(command, arguments);

        bool success = output.Contains("Process completed successfully.");
        bool warning = output.Contains("Process completed with warning");

        string message = "WwiseUnity: SoundBanks generation " + (success ? "successful" : (warning ? "has warning(s)" : "error")) + ":\n" + output;

        if (success)
        {
            AkSoundEngine.Log(message);
        }
        else if (warning)
        {
            AkSoundEngine.LogWarning(message);
        }
        else
        {
            AkSoundEngine.LogError(message);
        }

        AssetDatabase.Refresh();
    }

    // Reads the user settings (not the project settings) to check if there is an override currently defined for the soundbank generation folders.
    public static bool IsSoundbankOverrideEnabled(string wwiseProjectPath)
    {
        string userConfigFile = Path.Combine(Path.GetDirectoryName(wwiseProjectPath), Path.GetFileNameWithoutExtension(wwiseProjectPath) + "." + Environment.UserName + ".wsettings");

        if (!File.Exists(userConfigFile))
        {
            return false;
        }

        XmlDocument userConfigDoc = new XmlDocument();
        userConfigDoc.Load(userConfigFile);
        XPathNavigator userConfigNavigator = userConfigDoc.CreateNavigator();

        XPathNavigator userConfigNode = userConfigNavigator.SelectSingleNode(XPathExpression.Compile("//Property[@Name='SoundBankPathUserOverride' and @Value = 'True']"));

        return (userConfigNode != null);
    }

    // For a list of platform targets, gather all the required folder names for the generated soundbanks. The goal is to know the list of required
    // folder names for a list of platforms. The size of the returned array is not guaranteed to be the safe size at the targets array since
    // a single platform is no guaranteed to output a single soundbank folder.
    private static void UpdateSoundbanksDestinationFolders(string WwiseProjectPath)
    {
        try
        {
            if (WwiseProjectPath.Length == 0)
                return;

            if (!File.Exists(WwiseProjectPath))
                return;

            s_ProjectBankPaths.Clear();

            XmlDocument doc = new XmlDocument();
            doc.Load(WwiseProjectPath);
            XPathNavigator Navigator = doc.CreateNavigator();

            // Gather the mapping of Custom platform to Base platform
            XPathNodeIterator itpf = Navigator.Select("//Platform");
            s_BaseToCustomPF.Clear();
            foreach (XPathNavigator node in itpf)
            {
                HashSet<string> customList = null;
                string basePF = node.GetAttribute("ReferencePlatform", "");
                if (!s_BaseToCustomPF.TryGetValue(basePF, out customList))
                {
                    customList = new HashSet<string>();
                    s_BaseToCustomPF[basePF] = customList;
                }

                customList.Add(node.GetAttribute("Name", ""));
            }

            // Navigate the wproj file (XML format) to where generated soundbank paths are stored
            XPathNodeIterator it = Navigator.Select("//Property[@Name='SoundBankPaths']/ValueList/Value");
            foreach (XPathNavigator node in it)
            {
                string path = node.Value;
                AkBasePathGetter.FixSlashes(ref path);
                string pf = node.GetAttribute("Platform", "");
                s_ProjectBankPaths[pf] = path;
            }
        }
        catch (Exception ex)
        {
            // Error happened, return empty string
            AkSoundEngine.LogError("Wwise: Error while reading project " + WwiseProjectPath + ".  Exception: " + ex.Message);
        }
    }
    
    // Parses the .wproj to find out where soundbanks are generated for the given path.
    public static string GetWwiseSoundBankDestinationFolder(string Platform, string WwiseProjectPath)
	{
		try
		{
            UpdateSoundbanksDestinationFolders(WwiseProjectPath);
            return s_ProjectBankPaths[Platform];
		}
		catch( Exception )
		{
		}
        // Error happened, return empty string
        return "";
	}


    static public IDictionary<string, string> GetAllBankPaths()
    {
        AudioSetting Settings = AudioSetting.LoadSettings();
        string WwiseProjectFullPath = AkUtilities.GetFullPath(Application.dataPath, Settings.WwiseProjectPath);
        UpdateSoundbanksDestinationFolders(WwiseProjectFullPath);
        return s_ProjectBankPaths;
    }
}

}
#endif //UNITY_EDITOR
#endif // #if ! (UNITY_DASHBOARD_WIDGET || UNITY_WEBPLAYER || UNITY_WII || UNITY_WIIU || UNITY_NACL || UNITY_FLASH || UNITY_BLACKBERRY) // Disable under unsupported platforms.